import os
import csv
import time
import qrcode
import requests
import threading
from urllib.parse import urlparse, parse_qs
from PIL import Image
from utils import USER_AGENT, parse_cookie_string, extract_csrf_from_cookie,extract_sess_data_from_cookie
import json

class LoginManager:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super().__new__(cls, *args, **kwargs)
            cls._instance._init_login()
        return cls._instance

    def _init_login(self):
        self.csv_path = "login_info.csv"  # 登录信息保存路径
        self.cookies = {}  # 当前Cookie字典
        self.csrf_token = ""  # 当前CSRF Token
        self.sess_data = ''   # 当前SESSDATA
        self._load_cookie_from_csv()  # 启动时加载保存的Cookie

    def _load_cookie_from_csv(self) -> tuple[bool, str]:
        """从CSV加载最近的有效登录Cookie"""
        if not os.path.exists(self.csv_path):
            return False, "登录信息文件不存在"

        try:
            with open(self.csv_path, 'r', encoding='utf-8') as f:
                reader = csv.DictReader(f)
                rows = list(reader)
                if not rows:
                    return False, "登录信息为空"

                # 查找最新的有效登录（Status=1）
                latest_row = None
                for row in reversed(rows):
                    if row.get('Status') == '1':
                        latest_row = row
                        break
                if not latest_row:
                    return False, "无有效登录信息"

                # 解析Cookie字符串
                cookie_str = latest_row.get('Cookies', '')
                self.cookies = parse_cookie_string(cookie_str)
                self.csrf_token = extract_csrf_from_cookie(self.cookies)
                self.sess_data = extract_sess_data_from_cookie(cookie_str)

                return True, "登录信息加载成功"
        except Exception as e:
            return False, f"加载登录信息失败: {str(e)}"

    def save_cookie_to_csv(self) -> tuple[bool, str]:
        """将当前Cookie保存到CSV"""
        if not self.cookies:
            return False, "无可用Cookie"

        try:
            # 准备数据
            dedeuserid = self.cookies.get("DedeUserID", "")
            bili_jct = self.cookies.get("bili_jct", "")
            cookie_str = "; ".join([f"{k}={v}" for k, v in self.cookies.items()])
            login_time = time.strftime("%Y-%m-%d %H:%M:%S")

            # 检查文件是否存在（不存在则创建表头）
            file_exists = os.path.exists(self.csv_path)
            with open(self.csv_path, 'a', newline='', encoding='utf-8') as f:
                writer = csv.writer(f)
                if not file_exists:
                    writer.writerow(["DedeUserID", "bili_jct", "Cookies", "Status", "LoginTime"])
                writer.writerow([dedeuserid, bili_jct, cookie_str, "1", login_time])

            return True, f"登录信息已保存到 {self.csv_path}"
        except Exception as e:
            return False, f"保存登录信息失败: {str(e)}"

    def import_cookie(self, cookie_str: str) -> tuple[bool, str]:
        """导入Cookie字符串并更新登录状态"""
        if not cookie_str.strip():
            return False, "Cookie字符串为空"

        try:
            self.cookies = parse_cookie_string(cookie_str)
            self.csrf_token = extract_csrf_from_cookie(self.cookies)
            return True, "Cookie导入成功"
        except Exception as e:
            return False, f"Cookie解析失败: {str(e)}"

    def test_cookie(self) -> tuple[bool, str]:
        """测试Cookie有效性（调用B站个人信息API）"""
        if not self.cookies:
            return False, "未导入Cookie"

        try:
            url = "https://api.bilibili.com/x/space/myinfo"
            response = requests.get(url, cookies=self.cookies, headers={"User-Agent": USER_AGENT})
            data = response.json()
            print(data)
            if data['code'] == 0:
                user_info = data['data']
                return True, f"Cookie有效：用户名[{user_info['name']}]，UID[{user_info['mid']}]"
            else:
                return False, f"Cookie无效：{data['message']}"
        except Exception as e:
            return False, f"测试失败: {str(e)}"

    def get_user_info_with_cookie(self) -> str:
        """通过Cookie获取用户信息并返回JSON数据"""
        result = {
            "success": False,
            "message": "",
            "data": None
        }

        if not self.cookies:
            result["message"] = "未导入Cookie"
            return json.dumps(result, ensure_ascii=False)

        try:
            url = "https://api.bilibili.com/x/space/myinfo"
            response = requests.get(
                url,
                cookies=self.cookies,
                headers={"User-Agent": USER_AGENT}
            )
            response.raise_for_status()  # 抛出HTTP错误状态码
            data = response.json()

            if data.get('code') == 0:
                user_info = data.get('data', {})
                # 提取关键用户信息
                result["success"] = True
                result["message"] = "获取用户信息成功"
                result["data"] = {
                    "username": user_info.get("name"),
                    "uid": user_info.get("mid"),
                    "avatar": user_info.get("face"),
                    "level": user_info.get("level"),
                    "sign": user_info.get("sign")
                }
            else:
                result["message"] = f"API返回错误: {data.get('message', '未知错误')}"

        except requests.exceptions.RequestException as e:
            result["message"] = f"网络请求失败: {str(e)}"
        except Exception as e:
            result["message"] = f"处理数据失败: {str(e)}"

        # 返回JSON字符串
        return json.dumps(result, ensure_ascii=False, indent=2)

    def generate_qr_code(self, qr_label) -> tuple[bool, str]:
        """生成二维码（供外部GUI标签显示）"""

        def _generate():
            try:
                # 1. 获取二维码生成链接
                url = "https://passport.bilibili.com/x/passport-login/web/qrcode/generate?source=main_web"
                response = requests.get(url, headers={"User-Agent": USER_AGENT})
                data = response.json()

                if data['code'] != 0:
                    qr_label.config(text=f"二维码生成失败: {data['message']}")
                    return

                qr_url = data['data']['url']
                qr_key = data['data']['qrcode_key']
                qr_label.config(text="二维码已生成，请用B站APP扫描")

                # 2. 生成二维码图片
                qr = qrcode.QRCode(version=1, error_correction=qrcode.constants.ERROR_CORRECT_L, box_size=8, border=2)
                qr.add_data(qr_url)
                qr.make(fit=True)
                img = qr.make_image(fill_color="black", back_color="white")
                img = img.resize((180, 180), Image.Resampling.LANCZOS)

                # 3. 显示到GUI标签
                from PIL import ImageTk
                photo = ImageTk.PhotoImage(img)
                qr_label.config(image=photo)
                qr_label.image = photo  # 防止GC回收

                # 4. 轮询二维码登录状态
                self._poll_qr_status(qr_key, qr_label)
            except Exception as e:
                qr_label.config(text=f"二维码生成异常: {str(e)}")

        # 子线程执行（避免阻塞GUI）
        threading.Thread(target=_generate, daemon=True).start()
        return True, "正在生成二维码..."

    def _poll_qr_status(self, qr_key: str, qr_label) -> None:
        """轮询二维码登录状态（扫码/确认/过期）"""

        def _poll():
            try:
                headers = {"User-Agent": USER_AGENT}
                for _ in range(30):  # 轮询30次（5秒/次，共150秒）
                    time.sleep(5)
                    poll_url = f"https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key={qr_key}&source=main_web"
                    response = requests.get(poll_url, headers=headers)
                    data = response.json()

                    # 登录成功（获取Cookie）
                    if data.get('data', {}).get('url'):

                        login_url = data['data']['url']

                        # 从响应Cookie和URL参数提取Cookie
                        response_cookies = {c.name: c.value for c in response.cookies}
                        parsed_url = urlparse(login_url)
                        url_params = parse_qs(parsed_url.query)


                        # 合并Cookie（URL参数优先）
                        self.cookies = response_cookies
                        for key in ['DedeUserID', 'DedeUserID__ckMd5', 'SESSDATA', 'bili_jct']:
                            if key in url_params:
                                self.cookies[key] = url_params[key][0]

                        self.csrf_token = extract_csrf_from_cookie(self.cookies)
                        self.save_cookie_to_csv()  # 自动保存登录信息
                        qr_label.config(text="二维码登录成功！")
                        return
                    # 等待确认
                    elif data.get('code') == 86090 or data.get('code') == 86101:
                        qr_label.config(text="已扫描二维码，请在APP确认登录")
                    # 二维码过期
                    elif data.get('code') == 86038:
                        qr_label.config(text="二维码已过期，请重新生成")
                        return
                    # 其他错误
                    else:
                        qr_label.config(text=f"登录状态: {data.get('message', '未知')}")

                qr_label.config(text="二维码登录超时（150秒）")
            except Exception as e:
                qr_label.config(text=f"轮询异常: {str(e)}")

        threading.Thread(target=_poll, daemon=True).start()

    def clear_cookie(self) -> None:
        """清除当前Cookie"""
        self.cookies = {}
        self.csrf_token = ""